/*
 * Daniel R Padilla
 *
 * Copyright (c) 2008, Daniel R Padilla
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.databasefacade.select;


import org.databasefacade.table.Table;
import org.databasefacade.table.column.Column;


/**
 * All the operators in this class have
 * ansiSQL standard definitions so the
 * class is not declared as abstract
 * because ansiSQL-92 is sufficient.
 */
public class Comparison
{
	private String ansiStandardSQL = null;
	private String nativeSQL = null;
    
	// These variables must be set if available 
	// because they are access by the translators.
	public boolean isColumnOnlyCompare = false;
	public boolean hasTablesAssigned = false;
	public Table leftSideTable = null;
	public Column leftSideColumn = null;
	public Table rightSideTable = null;
	public Column rightSideColumn = null;
	public Column additionalColumn = null;
	public int operation = 0;
    
	public void alwaysTrue()
	{
		reset();
		ansiStandardSQL = "1=1";
	}

	/*
	 *  Equal Comparisons 
	 */
	public void equalToField(Table table1, Column field1, Table table2, Column field2)
	{
		reset();
		setColumnOnly(table1, field1, table2, field2, Operator.EQUAL);
		ansiStandardSQL = Operator.assign(table1, field1, table2, field2, Operator.EQUAL);
	}

	public void equalToValue(Table table, Column field, Object value)
	{
		reset();
		setOther(table, field, null, null, Operator.EQUAL);
		ansiStandardSQL = Operator.assign(table, field, value, Operator.EQUAL);
	}

	public void equalToParam(Table table, Column field)
	{
		reset();
		setOther(table, field, null, null, Operator.EQUAL);
		ansiStandardSQL = Operator.assign(table, field, "?", Operator.EQUAL);
	}

	public void equalToField(Column field1, Column field2)
	{
		reset();
		setColumnOnly(null, field1, null, field2, Operator.EQUAL);
		ansiStandardSQL = Operator.assign(field1, field2, Operator.EQUAL);
	}

	public void equalToValue(Column field, Object value)
	{
		reset();
		setOther(null, field, null, null, Operator.EQUAL);
		ansiStandardSQL = Operator.assign(field, value, Operator.EQUAL);
	}

	public void equalToParam(Column field)
	{
		setOther(null, field, null, null, Operator.EQUAL);
		ansiStandardSQL = Operator.assign(field, "?", Operator.EQUAL);
	}
	
	/*
	 *  Not Equal Comparisons 
	 */
	public void notEqualToField(Table table1, Column field1, Table table2, Column field2)
	{
		reset();
		setColumnOnly(table1, field1, table2, field2, Operator.NOT_EQUAL);
		ansiStandardSQL = Operator.assign(table1, field1, table2, field2,
			Operator.NOT_EQUAL);
	}

	public void notEqualToValue(Table table, Column field, Object value)
	{
		reset();
		setOther(table, field, null, null, Operator.NOT_EQUAL);
		ansiStandardSQL = Operator.assign(table, field, value, Operator.NOT_EQUAL);
	}

	public void notEqualToParam(Table table, Column field)
	{
		reset();
		setOther(table, field, null, null, Operator.NOT_EQUAL);
		ansiStandardSQL = Operator.assign(table, field, "?", Operator.NOT_EQUAL);
	}

	public void notEqualToField(Column field1, Column field2)
	{
		reset();
		setColumnOnly(null, field1, null, field2, Operator.NOT_EQUAL);
		ansiStandardSQL = Operator.assign(field1, field2, Operator.NOT_EQUAL);
	}

	public void notEqualToValue(Column field, Object value)
	{
		reset();
		setOther(null, field, null, null, Operator.NOT_EQUAL);
		ansiStandardSQL = Operator.assign(field, value, Operator.NOT_EQUAL);
	}

	public void notEqualToParam(Column field)
	{
		reset();
		setOther(null, field, null, null, Operator.NOT_EQUAL);
		ansiStandardSQL = Operator.assign(field, "?", Operator.NOT_EQUAL);
	}
	
	/*
	 * Less Than Comparisons 
	 */
	public void lessThanField(Table table1, Column field1, Table table2, Column field2)
	{
		reset();
		setColumnOnly(table1, field1, table2, field2, Operator.LESS_THAN);
		ansiStandardSQL = Operator.assign(table1, field1, table2, field2,
			Operator.LESS_THAN);
	}

	public void lessThanValue(Table table, Column field, Object value)
	{
		reset();
		setOther(table, field, null, null, Operator.LESS_THAN);
		ansiStandardSQL = Operator.assign(table, field, value, Operator.LESS_THAN);
	}

	public void lessThanParam(Table table, Column field)
	{
		reset();
		setOther(table, field, null, null, Operator.LESS_THAN);
		ansiStandardSQL = Operator.assign(table, field, "?", Operator.LESS_THAN);
	}

	public void lessThanField(Column field1, Column field2)
	{
		reset();
		setColumnOnly(null, field1, null, field2, Operator.LESS_THAN);
		ansiStandardSQL = Operator.assign(field1, field2, Operator.LESS_THAN);
	}

	public void lessThanValue(Column field, Object value)
	{
		reset();
		setOther(null, field, null, null, Operator.LESS_THAN);
		ansiStandardSQL = Operator.assign(field, value, Operator.LESS_THAN);
	}

	public void lessThanParam(Column field)
	{
		reset();
		setOther(null, field, null, null, Operator.LESS_THAN);
		ansiStandardSQL = Operator.assign(field, "?", Operator.LESS_THAN);
	}
	
	/*
	 *  Less Than Or Equal To Comparisons 
	 */
	public void lessThanOrEqualToField(Table table1, Column field1, Table table2, Column field2)
	{
		reset();
		setColumnOnly(table1, field1, table2, field2, Operator.LESS_THAN_EQUAL_TO);
		ansiStandardSQL = Operator.assign(table1, field1, table2, field2,
			Operator.LESS_THAN_EQUAL_TO);
	}

	public void lessThanOrEqualToValue(Table table, Column field, Object value)
	{
		reset();
		setOther(table, field, null, null, Operator.LESS_THAN_EQUAL_TO);
		ansiStandardSQL = Operator.assign(table, field, value, Operator.LESS_THAN_EQUAL_TO);
	}

	public void lessThanOrEqualToParam(Table table, Column field)
	{
		reset();
		setOther(table, field, null, null, Operator.LESS_THAN_EQUAL_TO);
		ansiStandardSQL = Operator.assign(table, field, "?", Operator.LESS_THAN_EQUAL_TO);
	}

	public void lessThanOrEqualToField(Column field1, Column field2)
	{
		reset();
		setColumnOnly(null, field1, null, field2, Operator.LESS_THAN_EQUAL_TO);
		ansiStandardSQL = Operator.assign(field1, field2, Operator.LESS_THAN_EQUAL_TO);
	}

	public void lessThanOrEqualToValue(Column field, Object value)
	{
		reset();
		setOther(null, field, null, null, Operator.LESS_THAN_EQUAL_TO);
		ansiStandardSQL = Operator.assign(field, value, Operator.LESS_THAN_EQUAL_TO);
	}

	public void lessThanOrEqualToParam(Column field)
	{
		reset();
		setOther(null, field, null, null, Operator.LESS_THAN_EQUAL_TO);
		ansiStandardSQL = Operator.assign(field, "?", Operator.LESS_THAN_EQUAL_TO);
	}
	
	/** Greater Than Comparisons */
	public void greaterThanField(Table table1, Column field1, Table table2, Column field2)
	{
		reset();
		setColumnOnly(table1, field1, table2, field2, Operator.GREATER_THAN);
		ansiStandardSQL = Operator.assign(table1, field1, table2, field2,
			Operator.GREATER_THAN);
	}

	public void greaterThanValue(Table table, Column field, Object value)
	{
		reset();
		setOther(table, field, null, null, Operator.GREATER_THAN);
		ansiStandardSQL = Operator.assign(table, field, value, Operator.GREATER_THAN);
	}

	public void greaterThanParam(Table table, Column field)
	{
		reset();
		setOther(table, field, null, null, Operator.GREATER_THAN);
		ansiStandardSQL = Operator.assign(table, field, "?", Operator.GREATER_THAN);
	}

	public void greaterThanField(Column field1, Column field2)
	{
		reset();
		setColumnOnly(null, field1, null, field2, Operator.GREATER_THAN);
		ansiStandardSQL = Operator.assign(field1, field2, Operator.GREATER_THAN);
	}

	public void greaterThanValue(Column field, Object value)
	{
		reset();
		setOther(null, field, null, null, Operator.GREATER_THAN);
		ansiStandardSQL = Operator.assign(field, value, Operator.GREATER_THAN);
	}

	public void greaterThanParam(Column field)
	{
		reset();
		setOther(null, field, null, null, Operator.GREATER_THAN);
		ansiStandardSQL = Operator.assign(field, "?", Operator.GREATER_THAN);
	}
	
	/** Greater Than Or Equal To Comparisons */
	public void greaterThanOrEqualToField(Table table1, Column field1, Table table2, Column field2)
	{
		reset();
		setColumnOnly(table1, field1, table2, field2, Operator.GREATER_THAN_EQUAL_TO);
		ansiStandardSQL = Operator.assign(table1, field1, table2, field2,
			Operator.GREATER_THAN_EQUAL_TO);
	}

	public void greaterThanOrEqualToValue(Table table, Column field, Object value)
	{
		reset();
		setOther(table, field, null, null, Operator.GREATER_THAN_EQUAL_TO);
		ansiStandardSQL = Operator.assign(table, field, value,
			Operator.GREATER_THAN_EQUAL_TO);
	}

	public void greaterThanOrEqualToParam(Table table, Column field)
	{
		reset();
		setOther(table, field, null, null, Operator.GREATER_THAN_EQUAL_TO);
		ansiStandardSQL = Operator.assign(table, field, "?",
			Operator.GREATER_THAN_EQUAL_TO);
	}

	public void greaterThanOrEqualToField(Column field1, Column field2)
	{
		reset();
		setColumnOnly(null, field1, null, field2, Operator.GREATER_THAN_EQUAL_TO);
		ansiStandardSQL = Operator.assign(field1, field2, Operator.GREATER_THAN_EQUAL_TO);
	}

	public void greaterThanOrEqualToValue(Column field, Object value)
	{
		reset();
		setOther(null, field, null, null, Operator.GREATER_THAN_EQUAL_TO);
		ansiStandardSQL = Operator.assign(field, value, Operator.GREATER_THAN_EQUAL_TO);
	}

	public void greaterThanOrEqualToParam(Column field)
	{
		reset();
		setOther(null, field, null, null, Operator.GREATER_THAN_EQUAL_TO);
		ansiStandardSQL = Operator.assign(field, "?", Operator.GREATER_THAN_EQUAL_TO);
	}
    
	/** Is Null and Is Not Null */
	public void isEqualNull(Column field)
	{
		setOther(null, field, null, null, 0);
		ansiStandardSQL = field.getTableAlias() + "." + field.getColumnName() + " IS NULL";
	}

	public void isNotEqualNull(Column field)
	{
		setOther(null, field, null, null, 0);
		ansiStandardSQL = field.getTableAlias() + "." + field.getColumnName()
			+ " IS NOT NULL";
	}
	
	/** In Comparisons */
	public void in(Table table, Column field, String[] values)
	{
		reset();
		ansiStandardSQL = compareIn("IN", table, field, values);
	}

	public void in(Table table, Column field, Select subselect)
	{
		reset();
		ansiStandardSQL = compareIn("IN", table, field, subselect);
	}

	/** Not In Comparisons */
	public void notIn(Table table, Column field, String[] values)
	{
		reset();
		ansiStandardSQL = compareIn("NOT IN", table, field, values);
	}

	public void notIn(Table table, Column field, Select subselect)
	{
		reset();
		ansiStandardSQL = compareIn("NOT IN", table, field, subselect);
	}
    
	/** Between Comparisons */
	public void between(
		Table betweenTable, Column betweenField, 
		Table startTable, Column startField, 
		Table endTable, Column endField)
	{
		reset();
		ansiStandardSQL = compBetween("BETWEEN", betweenTable, betweenField, startTable,
			startField, endTable, endField);
	}

	public void between(
		Table betweenTable, Column betweenField,
		Table startTable, Column startField, 
		Column endFunc)
	{
		reset();
		ansiStandardSQL = compBetween("BETWEEN", betweenTable, betweenField, startTable,
			startField, endFunc);
		
	}	

	public void beetween(
		Table betweenTable, Column betweenField,
		Column startFunc, 
		Table endTable, Column endField)
	{
		reset();
		ansiStandardSQL = compBeetween("BETWEEN", betweenTable, betweenField, startFunc, 
			endTable, endField);		
	}	

	public void between(			
		Table betweenTable, Column betweenField,
		Column startFunc, Column endFunc)
	{
		reset();
		ansiStandardSQL = compBetween("NOT BETWEEN", betweenTable, betweenField, startFunc,
			endFunc);
	}
    
	/** Not Between Comparisons */
	public void notBetween(
		Table betweenTable, Column betweenField, 
		Table startTable, Column startField, 
		Table endTable, Column endField)
	{
		reset();
		ansiStandardSQL = compBetween("NOT BETWEEN", betweenTable, betweenField, 
			startTable, startField, endTable, endField);
	}

	public void notBetween(
		Table betweenTable, Column betweenField,
		Table startTable, Column startField, 
		Column endFunc)
	{
		reset();
		ansiStandardSQL = compBetween("NOT BETWEEN", betweenTable, betweenField,
			startTable, startField, endFunc);
		
	}	

	public void notBeetween(
		Table betweenTable, Column betweenField,
		Column startFunc, 
		Table endTable, Column endField)
	{
		reset();
		ansiStandardSQL = compBeetween("NOT BETWEEN", betweenTable, betweenField,
			startFunc, endTable, endField);		
	}	

	public void notBetween(			
		Table betweenTable, Column betweenField,
		Column startFunc, Column endFunc)
	{
		reset();
		ansiStandardSQL = compBetween("NOT BETWEEN", betweenTable, betweenField, startFunc,
			endFunc);
	}
	
	public String getSQL()
	{
		return ansiStandardSQL;
	}
    
	public void setNativeSQL(String ns)
	{
		nativeSQL = ns;
	}

	public String getNativeSQL()
	{
		return nativeSQL;
	}
	
	/** Build the IN Strings */
	private String compareIn(String comp, Table table, Column field, String[] values)
	{
		setOther(table, field, null, null, 0);
		return null;
	}

	private String compareIn(String comp, Table table, Column field, Select subselect)
	{
		setOther(table, field, null, null, 0);
		return null;
	}
	
	/** Build the between strings */
	private String compBetween(
		String comp,
		Table betweenTable, Column betweenField, 
		Table startTable, Column startField, 
		Table endTable, Column endField)
	{
		additionalColumn = endField.getCopy();
		setOther(betweenTable, betweenField, startTable, startField, 0);
		return null;
	}

	private String compBetween(
		String comp,
		Table betweenTable, Column betweenField,
		Table startTable, Column startField, 
		Column endFunc)
	{
		setOther(betweenTable, betweenField, startTable, startField, 0);
		return null;
	}	

	private String compBeetween(
		String comp,
		Table betweenTable, Column betweenField,
		Column startFunc, 
		Table endTable, Column endField)
	{
		setOther(betweenTable, betweenField, endTable, endField, 0);
		return null;
	}	

	private String compBetween(	
		String comp,
		Table betweenTable, Column betweenField,
		Column startFunc, Column endFunc)
	{
		setOther(betweenTable, betweenField, null, null, 0);
		return null;
	}
	
	private void reset()
	{
		isColumnOnlyCompare = false;
		hasTablesAssigned = false;
		leftSideTable = null;
		leftSideColumn = null;
		rightSideTable = null;
		rightSideColumn = null;
		operation = 0;
	}
	
	/**
	 * Assigns data that is critical to the translators
	 * so that they can build the queries. New methods
	 * have to make the proper assignments.
	 */
	public void setColumnOnly(Table table1, Column field1, Table table2, Column field2, int opType)
	{
		reset();
		isColumnOnlyCompare = true;
		if ( table1 != null && table2 != null ) {
			hasTablesAssigned = true;
		} else {
			hasTablesAssigned = false;
		}
		if ( table1 != null ) {
			leftSideTable = table1.getCopy();
		}
		leftSideColumn = field1.getCopy();
		if ( table2 != null ) {
			rightSideTable = table2.getCopy();
		}
		rightSideColumn = field2.getCopy();
		operation = opType;
	}
	
	/**
	 * Assigns data that is critical to the translators
	 * so that they can build the queries. New methods
	 * have to make the proper assignments.
	 */
	public void setOther(Table table1, Column field1, Table table2, Column field2, int opType)
	{
		reset();
		isColumnOnlyCompare = false;
		if ( table1 != null && table2 != null ) {
			hasTablesAssigned = true;
		} else {
			hasTablesAssigned = false;
		}
		if ( table1 != null ) {
			leftSideTable = table1.getCopy();
		}
		if ( field1 != null ) {
			leftSideColumn = field1.getCopy();
		}
		if ( table2 != null ) {
			rightSideTable = table2.getCopy();
		}
		if ( field2 != null ) {
			rightSideColumn = field2.getCopy();
		}
		operation = opType;
	}
	
	public boolean isRestrictedToTable(Table table)
	{
		return associated(table, leftSideColumn) && associated(table, rightSideColumn)
			&& associated(table, additionalColumn);
	}
	
	private boolean associated(Table table, Column col)
	{
		if ( col != null && table != null ) {
			if ( !col.getTableName().equals(table.getTableName()) ) {
				return false;
			}		
		}
		return true;
	}
	
}
